package org.springframework.webflow.samples.booking;

import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;

import sun.util.logging.resources.logging;

/**
 * A JPA-based implementation of the Booking Service. Delegates to a JPA entity manager to issue data access calls
 * against the backing repository. The EntityManager reference is provided by the managing container (Spring)
 * automatically.
 */
@Service("bookingService")
@Repository
public class JpaBookingService implements BookingService {
	
	private static final Logger logging = LoggerFactory.getLogger(JpaBookingService.class);

    private EntityManager em;

    @PersistenceContext
    public void setEntityManager(EntityManager em) {
    	this.em = em;
    }

    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public List<Booking> findBookings(String username) {
    	logging.info("username=" + username);
		if (username != null) {
		    return em.createQuery("select b from Booking b where b.user.username = :username order by b.checkinDate").setParameter("username", username).getResultList();
		} else {
		    return null;
		}
    }

    @Transactional(readOnly = true)
    @SuppressWarnings("unchecked")
    public List<Hotel> findHotels(SearchCriteria criteria) {
		String pattern = getSearchPattern(criteria);
		int startIndex = criteria.getPage() * criteria.getPageSize();
		List lst = em
			.createQuery(
				"select h from Hotel h where lower(h.name) like :pattern or lower(h.city) like :pattern "
					+ " or lower(h.zip) like :pattern or lower(h.address) like :pattern")
			.setParameter("pattern", pattern).setFirstResult(startIndex).setMaxResults(criteria.getPageSize())
			.getResultList();
		
		logging.info("lst size=" + lst.size());
		return lst;
    }

    @Transactional(readOnly = true)
    public Hotel findHotelById(Long id) {
    	logging.info("findHotelById ="+id);
    	return em.find(Hotel.class, id);
    }

    @Transactional(readOnly = true)
    public Booking createBooking(Long hotelId, String username) {
		Hotel hotel = em.find(Hotel.class, hotelId);
		User user = findUser(username);
		Booking booking = new Booking(hotel, user);
		return booking;
    }

    @Transactional
    public void persistBooking(Booking booking) {
    	em.persist(booking);
    }

    @Transactional
    public void cancelBooking(Long id) {
		Booking booking = em.find(Booking.class, id);
		if (booking != null) {
		    em.remove(booking);
		}
    }

    // helpers
    private String getSearchPattern(SearchCriteria criteria) {
		if (StringUtils.hasText(criteria.getSearchString())) {
		    return "%" + criteria.getSearchString().toLowerCase().replace('*', '%') + "%";
		} else {
		    return "%";
		}
    }

    private User findUser(String username) {
		return (User) em.createQuery("select u from User u where u.username = :username")
			.setParameter("username", username).getSingleResult();
    }

}